package tech.wetech.admin.web.controller.system;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import tech.wetech.admin.common.Constants;
import tech.wetech.admin.common.utils.JsonUtil;
import tech.wetech.admin.common.utils.ZipUtils;
import tech.wetech.admin.generator.bridge.MybatisGeneratorBridge;
import tech.wetech.admin.generator.model.GeneratorConfig;
import tech.wetech.admin.generator.util.JdbcConfigHelper;
import tech.wetech.admin.model.system.User;
import tech.wetech.admin.web.controller.base.BaseController;
import tech.wetech.admin.web.dto.system.GeneratorDto;

import static org.apache.poi.xdgf.util.Util.sanitizeFilename;
import static tech.wetech.admin.generator.Generator.LOGGER;

/**
 * 代码生成器控制层
 * <p>
 * Created by cjbi on 2018/1/8.
 */
@Controller
@RequestMapping("/generator")
public class GeneratorController extends BaseController{

    @Autowired
    private MybatisGeneratorBridge mybatisGeneratorBridge;

    @RequestMapping(method = RequestMethod.GET)
    public String toPage(Model model) throws SQLException {
        model.addAttribute("tableNames", JdbcConfigHelper.getTableNames());
        return "system/generator";
    }

    private String base64DecodeToString(String text) throws UnsupportedEncodingException {
        byte[] textByte = text.getBytes("UTF-8");
        Base64.Decoder decoder = Base64.getDecoder();
        decoder.decode(textByte);
        return new String(decoder.decode(textByte), "UTF-8");
    }

/*    @PostMapping("/generate")
    public ResponseEntity<?> generateCode(@RequestBody GeneratorDto dto,
                                          HttpServletRequest request) {
        try {
            String tmpPath = request.getSession().getServletContext().getRealPath("/WEB-INF/tmp");
            User user = (User) request.getAttribute(Constants.CURRENT_USER);
            String srcPath = tmpPath + File.separator + user.getUsername() + File.separator + "code";
            String destPath = tmpPath + File.separator + user.getUsername() + File.separator + "code" + ZipUtils.EXT;

            dto.setProjectFolder(srcPath);
            GeneratorConfig config = getConfig(dto);

            mybatisGeneratorBridge.setGeneratorConfig(config);
            mybatisGeneratorBridge.generate();

            ZipUtils.compress(srcPath, destPath);
            File file = new File(destPath);

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getName())
                    .contentLength(file.length())
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(FileUtils.readFileToByteArray(file));

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("生成代码失败: " + e.getMessage());
        }
    }*/

    @PostMapping(value = "/generate",
            produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) // 明确声明输出二进制流
    public ResponseEntity<StreamingResponseBody> generateCode(
            @RequestBody GeneratorDto dto,
            HttpServletRequest request) { // 移除非必要的HttpServletResponse参数

        // 获取路径参数
        String tmpPath = request.getServletContext().getRealPath("/WEB-INF/tmp");
        User user = (User) request.getAttribute(Constants.CURRENT_USER);
        Path srcPath = Paths.get(tmpPath, user.getUsername(), "code");

        // 使用Lambda（无需类序列化）
        StreamingResponseBody stream = out -> {
            try (ZipOutputStream zos = new ZipOutputStream(out)) {
                generateAndZip(dto, srcPath, zos);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                //cleanTempFiles(srcPath); // 提取清理逻辑到独立方法
            }
        };

        // 返回时明确指定CONTENT_TYPE
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=code.zip")
                .body(stream);
    }

    private void generateAndZip(GeneratorDto dto, Path srcPath, ZipOutputStream zos) throws Exception {
        // 1. 生成代码到指定路径
        dto.setProjectFolder(srcPath.toString());
        mybatisGeneratorBridge.setGeneratorConfig(getConfig(dto));
        mybatisGeneratorBridge.generate();

        // 2. 流式压缩文件
        Files.walk(srcPath)
                .filter(path -> !Files.isDirectory(path))
                .forEach(path -> {
                    try {
                        ZipEntry entry = new ZipEntry(srcPath.relativize(path).toString());
                        zos.putNextEntry(entry);
                        Files.copy(path, zos);
                        zos.closeEntry();
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                });
    }

    @RequestMapping(method = RequestMethod.GET, value = "/code.zip")
    public ResponseEntity<byte[]> downloadCode(HttpServletRequest request, HttpServletResponse response, String p) {
        ResponseEntity<byte[]> responseEntity = null;
        String tmpPath = request.getSession().getServletContext().getRealPath("/WEB-INF/tmp");
        User user = (User) request.getAttribute(Constants.CURRENT_USER);
        String srcPath = tmpPath + File.separator + user.getUsername() + File.separator + "code";
        String destPath = tmpPath + File.separator + user.getUsername() + File.separator + "code" + ZipUtils.EXT;
        try {
            if (StringUtils.isEmpty(p)) {
                throw new RuntimeException("参数p不能为空");
            }
            String json = base64DecodeToString(p);
            GeneratorDto dto = (GeneratorDto) JsonUtil.getInstance().json2obj(json, GeneratorDto.class);
            dto.setProjectFolder(srcPath);
            mybatisGeneratorBridge.setGeneratorConfig(getConfig(dto));
            mybatisGeneratorBridge.generate();
            ZipUtils.compress(srcPath, destPath);
            File file = new File(destPath);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", file.getName());
            responseEntity = new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
        } catch (Exception e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            try {
                String errMsg = String.format("生成代码失败，错误原因：%s", e.getMessage());
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                OutputStream out = response.getOutputStream();
                logger.error(errMsg);
                out.write(errMsg.getBytes("UTF-8"));
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            File file = new File(tmpPath + File.separator + user.getUsername());
            if (file.exists()) {
                FileUtils.deleteQuietly(file);
            }
        }
        return responseEntity;
    }

    private GeneratorConfig getConfig(GeneratorDto generatorDto) throws RuntimeException {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        GeneratorConfig generatorConfig = new GeneratorConfig();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<GeneratorDto>> violations = validator.validate(generatorDto);
        if (!CollectionUtils.isEmpty(violations)) {
            StringBuilder builder = new StringBuilder();
            violations.forEach(violation -> {
                String property = violation.getPropertyPath().toString();
                String message = violation.getMessage();
                builder.append(property);
                builder.append(message);
                builder.append(",");
            });
            if (builder.length() > 0) {
                builder.deleteCharAt(builder.length() - 1);
            }
            logger.error(String.format("参数校验失败:%s"),builder.toString());
            throw new RuntimeException(builder.toString());
        }
        BeanUtils.copyProperties(generatorDto, generatorConfig);
        /*if (!checkDirs(generatorConfig)) {
            return null;
        }*/

        if (!checkAndCreateDirs(generatorConfig)) {
            return null;
        }
        return generatorConfig;
    }

    /**
     * 检查并创建不存在的文件夹
     *
     * @return
     */
    /*private boolean checkDirs(GeneratorConfig config) {
        List<String> dirs = new ArrayList<>();
        dirs.add(config.getProjectFolder());
        dirs.add(FilenameUtils
                .normalize(config.getProjectFolder().concat("/").concat(config.getModelPackageTargetFolder())));
        dirs.add(FilenameUtils.normalize(config.getProjectFolder().concat("/").concat(config.getDaoTargetFolder())));
        dirs.add(FilenameUtils
                .normalize(config.getProjectFolder().concat("/").concat(config.getMappingXMLTargetFolder())));
        dirs.add(FilenameUtils.normalize(config.getProjectFolder().concat("/").concat(config.getJspTargetFolder())));
        boolean haveNotExistFolder = false;
        for (String dir : dirs) {
            File file = new File(dir);
            if (!file.exists()) {
                haveNotExistFolder = true;
            }
        }
        if (haveNotExistFolder) {

            try {
                for (String dir : dirs) {
                    FileUtils.forceMkdir(new File(dir));
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return true;
    }*/

    private boolean checkAndCreateDirs(GeneratorConfig config) {
        // 1. 定义所有需要检查的目录
        List<String> requiredDirs = Arrays.asList(
                config.getModelPackageTargetFolder(),
                config.getDaoTargetFolder(),
                config.getMappingXMLTargetFolder(),
                config.getServiceTargetFolder(),
                config.getServiceImplTargetFolder(),
                config.getControllerTargetFolder(), // 新增Controller目录检查
                config.getJspTargetFolder()
        );

        // 2. 使用Paths处理跨平台路径
        List<Path> dirPaths = requiredDirs.stream()
                .map(dir -> Paths.get(config.getProjectFolder(), dir.split("/")))
                .collect(Collectors.toList());

        // 3. 检查并创建目录
        try {
            for (Path dirPath : dirPaths) {
                if (!Files.exists(dirPath)) {
                    LOGGER.info("创建目录: {}", dirPath);
                    Files.createDirectories(dirPath); // 自动创建父目录

                    // 验证目录是否可写
                    if (!Files.isWritable(dirPath)) {
                        throw new IOException("目录不可写: " + dirPath);
                    }
                }
            }
            return true;
        } catch (IOException e) {
            LOGGER.error("目录处理失败", e);
            throw new RuntimeException("无法创建目录: " + e.getMessage(), e);
        }
    }
}
